# -*- coding: utf-8 -*-
# @File: main.py
# @Author: Liccsu
# @Time: 24/10/2022 下午10:16
# @Project: AI-01-AStar
import argparse as ap
import random
import re
from functools import wraps
from time import process_time
from typing import Callable

import numpy as np

from node import Node
from solution import Solution


def run_time(func: Callable) -> Callable:
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = process_time()
        result = func(*args, **kwargs)
        end_time = process_time()
        print('run time: {}s\n'.format(end_time - start_time))
        return result

    return wrapper


def is_can_be_solved(_start, _end):
    """
    :param _start: 初始节点
    :param _end: 目标节点
    :return: 两节点是否相互可达
    """
    return not ((_start.inverse_number() + _end.inverse_number()) % 2)


def arg_parse():
    """
    :return: 参数集合
    """
    _parser = ap.ArgumentParser()
    _parser.add_argument(
        "-m",
        "--mode",
        default="astar",
        type=str,
        choices=["astar", "bfs", "dfs"],
        required=False,
        help="选择搜索算法 [astar/bfs/dfs] 默认astar"
    )
    _parser.add_argument(
        "-i",
        "--initial",
        default="random",
        type=str,
        required=False,
        help="初始状态 [0~8] 以逗号隔开 默认随机"
    )
    _parser.add_argument(
        "-t",
        "--target",
        default="random",
        type=str,
        required=False,
        help="目标状态 [0~8] 以逗号隔开 默认随机"
    )
    args = _parser.parse_args()
    if args.initial == "random":
        initial_list = list(range(9))
        random.shuffle(initial_list)
    else:
        initial_str = args.initial.strip(' ')
        initial_list = re.split("[ ,，]", initial_str)

    if args.target == "random":
        target_list = list(range(9))
        random.shuffle(target_list)
    else:
        target_str = args.target.strip(' ')
        target_list = re.split("[ ,，]", target_str)

    initial_matrix = [[int(initial_list[3 * i + j]) for j in range(3)] for i in range(3)]
    target_matrix = [[int(target_list[3 * i + j]) for j in range(3)] for i in range(3)]
    _initial_node = Node(np.array(initial_matrix))
    _target_node = Node(np.array(target_matrix))

    return args.mode, Node(np.array(initial_matrix)), Node(np.array(target_matrix))


@run_time
def main():
    solution = Solution(
        begin_node=initial_node,
        end_node=target_node
    )
    solution.search(mode)  # 搜索
    # solution.a_star_search()  # A*搜索
    # solution.bfs_search(time_out=120)  # 宽度优先搜索
    # solution.dfs_search(max_depth=30, time_out=300)  # 深度优先搜索

    if solution.path_list:
        i = 0
        steps = ""
        print("\r")
        for node in solution.path_list:
            print("{}: ".format(i), end="")
            steps += node.action[-1] if i else steps
            i += 1
            node.print_step()
        print("总计搜索 {} 步".format(solution.searched_num))
        print("最优解为 {} 步".format(i - 1))
        print("最终解为:", steps)


if __name__ == '__main__':
    mode, initial_node, target_node = arg_parse()
    Node.target_node = target_node

    if is_can_be_solved(initial_node, target_node):
        print("有解")
        main()
    else:
        print("无解")
